﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

#if ENABLE_WINMD_SUPPORT
using Windows.Devices.Enumeration;
using Windows.Graphics.Imaging;
using Windows.Media;
using Windows.Media.Capture;
using Windows.Media.Capture.Frames;
using Windows.Media.Devices;
using Windows.Media.Devices.Core;
using Windows.Media.MediaProperties;
using Windows.Perception.Spatial;
using Windows.Storage.Streams;
#endif


public class RGBCamera
{
        //----- Unity UWP 公共区------------
    public bool IsCapturing { get; set; }
    public byte[] imageBytes { get; set; }
    public static uint CurrentFocusValue;
    public MyFrame _videoFrame = null;
    //------------ UWP --------------
#if ENABLE_WINMD_SUPPORT
        public MediaCapture _mediaCapture;
        public MediaFrameReader _mediaFrameReader;
        public FocusControl focusControl;
#endif



    public async Task InitializeMediaFrameReaderAsync()
        {
#if ENABLE_WINMD_SUPPORT

            if (_mediaCapture == null || _mediaCapture.CameraStreamState == CameraStreamState.Shutdown || _mediaCapture.CameraStreamState == CameraStreamState.NotStreaming)
            {
                if (_mediaCapture != null)
                {
                    _mediaCapture.Dispose();
                }

                MediaCaptureInitializationSettings settings = new MediaCaptureInitializationSettings();
                var allCameras = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);

                var selectedCamera = allCameras.FirstOrDefault(c => c.EnclosureLocation?.Panel == Windows.Devices.Enumeration.Panel.Back) ?? allCameras.FirstOrDefault();
            
                if (selectedCamera != null)
                {
                    settings.VideoDeviceId = selectedCamera.Id;
                 
                }

                _mediaCapture = new MediaCapture();
                await _mediaCapture.InitializeAsync(settings);


                var frameSourcePair = _mediaCapture.FrameSources.Where(source => source.Value.Info.SourceKind == MediaFrameSourceKind.Color).First();
                var subtype = MediaEncodingSubtypes.Bgra8;
                BitmapSize outputSize = new BitmapSize { Width = 1920, Height = 1080 };
                _mediaFrameReader = await _mediaCapture.CreateFrameReaderAsync(frameSourcePair.Value, subtype, outputSize);
                _mediaFrameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Realtime;

                await _mediaFrameReader.StartAsync();

                IsCapturing = true;              
         
                focusControl = _mediaCapture.VideoDeviceController.FocusControl;

            }
#endif
        }

    public async Task<MyFrame> GetLatestFrame()
        {
#if ENABLE_WINMD_SUPPORT
            SoftwareBitmap bitmap;
            try
            {
                CurrentFocusValue = focusControl.Value;

                var mediaFrameReference = _mediaFrameReader.TryAcquireLatestFrame();
                VideoFrame videoFrame = mediaFrameReference?.VideoMediaFrame?.GetVideoFrame();
                var spatialCoordinateSystem = mediaFrameReference?.CoordinateSystem;
                var cameraIntrinsics = mediaFrameReference?.VideoMediaFrame?.CameraIntrinsics;
           
                if (videoFrame == null || (videoFrame.Direct3DSurface == null && videoFrame.SoftwareBitmap == null))
                {
                    return _videoFrame;
                }

                if (videoFrame.Direct3DSurface != null && videoFrame.SoftwareBitmap == null)
                {
                     bitmap = await SoftwareBitmap.CreateCopyFromSurfaceAsync(videoFrame.Direct3DSurface);   
                }
                else
                {

                    bitmap = videoFrame.SoftwareBitmap;
                  
                }
                MyFrame returnFrame = new MyFrame
                {
                    spatialCoordinateSystem = spatialCoordinateSystem,
                    cameraIntrinsics = cameraIntrinsics,
                    bitmap = bitmap,
                };
                return returnFrame;
                
            }
            catch (Exception ex)
            {
                return _videoFrame;
            }
#endif
        return _videoFrame;
    }
    public async Task<bool> FixFocus(uint focus)
    {
#if ENABLE_WINMD_SUPPORT
        try
        {
            if(focusControl.Supported)
            {
                await focusControl.LockAsync();
                if(focus <= focusControl.Max && focus >=  focusControl.Min)
                {
                    await _mediaCapture.VideoDeviceController.FocusControl.SetValueAsync(focus);
                    return true;
                }
            }
        }
        catch
        {
               
            
        }
#endif
        return false;
    }
    public async Task<bool> AutoFocus()
    {
#if ENABLE_WINMD_SUPPORT
         try
         {
            if(focusControl.Supported)
            {
                 await focusControl.UnlockAsync();
                 var settings = new FocusSettings { Mode = FocusMode.Continuous, AutoFocusRange = AutoFocusRange.FullRange };
                 focusControl.Configure(settings);
                 await focusControl.FocusAsync();
                 return true;
             }      
         }
         catch
         {

         }
#endif
          return false;
    }
    public async Task<bool> SetExposureTime(long Value) // 100ns
{
#if ENABLE_WINMD_SUPPORT
    try
    {
        var exposureControl = _mediaCapture.VideoDeviceController.ExposureControl;
        if (exposureControl.Supported)
        {
            var value = TimeSpan.FromTicks(Value);
            await exposureControl.SetValueAsync(value);
            return true;
        }
    }
    catch
    {

    }

#endif
    return false;

}
    public async Task<bool> SetExposureCompensation(float Value) // 0
{
#if ENABLE_WINMD_SUPPORT
    try
    {
        var ExposureControl = _mediaCapture.VideoDeviceController.ExposureCompensationControl;
        if(ExposureControl.Supported)
        {
            await _mediaCapture.VideoDeviceController.ExposureCompensationControl.SetValueAsync(Value);
            return true;
        }  
    }
    catch
    {

    }
#endif
     return false;
}

    public async Task<byte[]> EncodSB2Bytes(MyFrame returnFrame)
    {
        byte[] byteArray = new byte[] { };
#if ENABLE_WINMD_SUPPORT
        var encodedStream = new InMemoryRandomAccessStream();
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, encodedStream);
        encoder.SetSoftwareBitmap(returnFrame.bitmap);
        await encoder.FlushAsync();
        encodedStream.Seek(0);

        byteArray = new byte[encodedStream.Size];
        await encodedStream.AsStream().ReadAsync(byteArray, 0, byteArray.Length);
#endif
        GC.Collect();
        return byteArray;
    }
    //SB2Byte_C3
#if ENABLE_WINMD_SUPPORT
        public async Task<byte[]> SB2Byte_C3(SoftwareBitmap bitmap, int scale = 1)
        {
            byte[] dimgs = new byte[3 * bitmap.PixelHeight * bitmap.PixelWidth / scale];
            byte[] imgs = new byte[4 * bitmap.PixelHeight * bitmap.PixelWidth];
            IBuffer buffer = imgs.AsBuffer();
            bitmap.CopyToBuffer(buffer);

            await Task.Run(() =>
            {
                for (uint row = 0; row < bitmap.PixelHeight; row++)
                {
                    if (row % scale != 0) continue;
                    for (uint col = 0; col < bitmap.PixelWidth; col++)
                    {
                        if (col % scale != 0) continue;
                        var currPixel1 = bitmap.PixelWidth * 4 * row + 4 * col;
                        var currPixel2 = (int)(bitmap.PixelWidth / scale) * 3 * (int)(row / scale) + 3 * (int)(col / scale);
                        dimgs[currPixel2 + 0] = imgs[currPixel1 + 0]; // Blue
                        dimgs[currPixel2 + 1] = imgs[currPixel1 + 1]; // Green
                        dimgs[currPixel2 + 2] = imgs[currPixel1 + 2]; // Red
                    }
                }
            });

            return dimgs;
        }
#endif

    //SB2Byte_C4
#if ENABLE_WINMD_SUPPORT
        public async Task<byte[]> SB2Byte_C4(SoftwareBitmap bitmap, int scale = 1)
        {
            byte[] dimgs = new byte[4 * bitmap.PixelHeight * bitmap.PixelWidth / scale];
            byte[] imgs = new byte[4 * bitmap.PixelHeight * bitmap.PixelWidth];
            IBuffer buffer = imgs.AsBuffer();
            bitmap.CopyToBuffer(buffer);

            await Task.Run(() =>
            {
                for (uint row = 0; row < bitmap.PixelHeight; row++)
                {
                    if (row % scale != 0) continue;
                    for (uint col = 0; col < bitmap.PixelWidth; col++)
                    {
                        if (col % scale != 0) continue;
                        var currPixel1 = bitmap.PixelWidth * 4 * row + 4 * col;
                        var currPixel2 = (int)(bitmap.PixelWidth / scale) * 4 * (int)(row / scale) + 4 * (int)(col / scale);
                        dimgs[currPixel2 + 0] = imgs[currPixel1 + 0]; // Blue
                        dimgs[currPixel2 + 1] = imgs[currPixel1 + 1]; // Green
                        dimgs[currPixel2 + 2] = imgs[currPixel1 + 2]; // Red
                    }
                }
            });

            return dimgs;
        }
#endif

    // SB2Byte_C1
#if ENABLE_WINMD_SUPPORT
    public async Task<byte[]> SB2Byte_C1(SoftwareBitmap bitmap, int scale = 1)
    {

        byte[] dimgs = new byte[bitmap.PixelHeight * bitmap.PixelWidth / scale];
        byte[] imgs = new byte[4 * bitmap.PixelHeight * bitmap.PixelWidth];
        IBuffer buffer = imgs.AsBuffer();
        bitmap.CopyToBuffer(buffer);

        int PixNum = 0;
        await Task.Run(() =>
        {
            for (uint row = 0; row < bitmap.PixelHeight; row++)
            {
                if (row % scale != 0) continue;
                for (uint col = 0; col < bitmap.PixelWidth; col++)
                {
                    if (col % scale != 0) continue;
                    var currPixel1 = bitmap.PixelWidth * 4 * row + 4 * col;

                    dimgs[PixNum++] = (byte)((imgs[currPixel1 + 0] * 50 + imgs[currPixel1 + 1] * 11 + imgs[currPixel1 + 2] * 30) / 100); // Blue
                }
            }
        });

        return dimgs;
        }

#endif

    public async Task StopMediaFrameReaderAsync()
        {
#if ENABLE_WINMD_SUPPORT
            if (_mediaCapture != null && _mediaCapture.CameraStreamState != CameraStreamState.Shutdown)
            {
                await _mediaFrameReader.StopAsync();
                _mediaFrameReader.Dispose();
                _mediaCapture.Dispose();
                _mediaCapture = null;
            }
            IsCapturing = false;
#endif
        }

}


public class MyFrame
{
#if ENABLE_WINMD_SUPPORT
    public SpatialCoordinateSystem spatialCoordinateSystem;
    public CameraIntrinsics cameraIntrinsics;
    public SoftwareBitmap bitmap;
#endif
}



